
#Web应用主入口
from flask import Flask, render_template, request, jsonify, session, redirect, url_for
from main import KnowledgeExplanationAgent
from config import FLASK_SECRET_KEY, DEFAULT_USERS, DEBUG_MODE
from model_status import model_status
from multimodal_enhancer import multimodal_enhancer
from knowledge_enhancer import zhiyuan_enhancer
import json
import os
from functools import wraps
from tempfile import NamedTemporaryFile
import subprocess


app = Flask(__name__)
app.config['SECRET_KEY'] = FLASK_SECRET_KEY

# 初始化知识解释智能体
agent = KnowledgeExplanationAgent()

def login_required(f):
    """登录验证装饰器"""
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if 'user_id' not in session:
            return redirect(url_for('login'))
        return f(*args, **kwargs)
    return decorated_function

@app.route('/')
def index():
    """重定向到登录页面"""
    return redirect(url_for('login'))

@app.route('/login', methods=['GET', 'POST'])
def login():
    """登录页面"""
    if request.method == 'POST':
        data = request.get_json()
        username = data.get('username')
        password = data.get('password')
        
        if username in DEFAULT_USERS and DEFAULT_USERS[username]['password'] == password:
            session['user_id'] = username
            session['user_role'] = DEFAULT_USERS[username]['role']
            session['user_name'] = DEFAULT_USERS[username]['name']
            return jsonify({'status': 'success', 'redirect': '/dashboard'})
        else:
            return jsonify({'status': 'error', 'message': '用户名或密码错误'}), 401
    
    return render_template('login.html')

@app.route('/logout')
def logout():
    """登出"""
    session.clear()
    return redirect(url_for('login'))

@app.route('/dashboard')
@login_required
def dashboard():
    """主页面（需要登录）"""
    return render_template('index.html', user=session)

@app.route('/code_editor')
@login_required
def code_editor():
    return render_template('code_editor.html', user=session)

@app.route('/code_practice')
@login_required
def code_practice():
    """代码练习界面"""
    return render_template('code_practice.html', user=session)

@app.route('/robot')
@login_required
def robot():
    """机器人助手界面"""
    return render_template('robot.html', user=session)

@app.route('/api/chat', methods=['POST'])
@login_required
def chat():
    """聊天API接口"""
    try:
        data = request.get_json()
        user_id = session.get('user_id', 'anonymous')
        message = data.get('message', '').strip()
        
        if not message:
            return jsonify({
                'status': 'error',
                'message': '消息不能为空'
            }), 400
        
        # 调用知识解释智能体处理问题
        result = agent.process_question(user_id, message)
        
        return jsonify(result)
        
    except Exception as e:
        return jsonify({
            'status': 'error',
            'message': f'服务器错误: {str(e)}'
        }), 500

@app.route('/api/status')
def status():
    """系统状态检查"""
    return jsonify({
        'status': 'success',
        'message': '系统运行正常',
        'version': '1.0.0',
        'model_status': model_status.get_status_summary()
    })

@app.route('/api/export', methods=['POST'])
def export_chat():
    """导出聊天记录"""
    try:
        data = request.get_json()
        chat_history = data.get('history', [])
        
        # 生成导出文件
        export_data = {
            'timestamp': data.get('timestamp'),
            'user_id': data.get('user_id'),
            'history': chat_history
        }
        
        return jsonify({
            'status': 'success',
            'data': export_data
        })
        
    except Exception as e:
        return jsonify({
            'status': 'error',
            'message': f'导出失败: {str(e)}'
        }), 500

@app.route('/api/knowledge', methods=['GET'])
def get_knowledge():
    """获取知识库信息"""
    try:
        # 这里可以返回知识库统计信息
        return jsonify({
            'status': 'success',
            'data': {
                'total_docs': len(agent.retriever.knowledge_base),
                'categories': ['definition', 'usage', 'error_debug', 'comparison', 'faq']
            }
        })
        
    except Exception as e:
        return jsonify({
            'status': 'error',
            'message': f'获取知识库信息失败: {str(e)}'
        }), 500

@app.route('/api/session/<session_id>', methods=['GET'])
def get_session(session_id):
    """获取会话信息"""
    try:
        context = agent.context_manager.get_context(session_id)
        summary = agent.context_manager.get_session_summary(session_id)
        
        return jsonify({
            'status': 'success',
            'data': {
                'context': context,
                'summary': summary
            }
        })
        
    except Exception as e:
        return jsonify({
            'status': 'error',
            'message': f'获取会话信息失败: {str(e)}'
        }), 500

@app.route('/api/session/<session_id>', methods=['DELETE'])
def clear_session(session_id):
    """清空会话"""
    try:
        agent.context_manager.clear_context(session_id)
        
        return jsonify({
            'status': 'success',
            'message': '会话已清空'
        })
        
    except Exception as e:
        return jsonify({
            'status': 'error',
            'message': f'清空会话失败: {str(e)}'
        }), 500

@app.route('/api/storage/stats', methods=['GET'])
@login_required
def get_storage_stats():
    """获取存储统计信息"""
    try:
        stats = agent.context_manager.get_storage_stats()
        return jsonify({
            'status': 'success',
            'stats': stats
        })
    except Exception as e:
        return jsonify({
            'status': 'error',
            'message': f'获取存储统计失败: {str(e)}'
        }), 500

@app.route('/api/render', methods=['POST'])
def render_markdown():
    """渲染Markdown为HTML"""
    try:
        data = request.get_json()
        markdown_content = data.get('markdown', '')
        
        if not markdown_content:
            return jsonify({
                'status': 'error',
                'message': 'Markdown内容不能为空'
            }), 400
        
        # 使用多模态增强器
        enhanced_content = multimodal_enhancer.enhance_content(markdown_content)
        
        return jsonify({
            'status': 'success',
            'html': enhanced_content['enhanced_html'],
            'code_blocks': enhanced_content['code_blocks'],
            'mermaid_diagrams': enhanced_content['mermaid_diagrams'],
            'tables': enhanced_content['tables']
        })
        
    except Exception as e:
        return jsonify({
            'status': 'error',
            'message': f'渲染失败: {str(e)}'
        }), 500

@app.route('/api/multimodal/styles')
def get_multimodal_styles():
    """获取多模态增强样式"""
    try:
        css_styles = multimodal_enhancer.generate_css_styles()
        return jsonify({
            'status': 'success',
            'css': css_styles
        })
    except Exception as e:
        return jsonify({
            'status': 'error',
            'message': f'获取样式失败: {str(e)}'
        }), 500

@app.route('/api/multimodal/scripts')
def get_multimodal_scripts():
    """获取多模态增强脚本"""
    try:
        js_functions = multimodal_enhancer.generate_js_functions()
        return jsonify({
            'status': 'success',
            'javascript': js_functions
        })
    except Exception as e:
        return jsonify({
            'status': 'error',
            'message': f'获取脚本失败: {str(e)}'
        }), 500

@app.route('/api/zhiyuan/stats', methods=['GET'])
@login_required
def get_zhiyuan_stats():
    """获取智源研究院知识统计"""
    try:
        stats = zhiyuan_enhancer.get_knowledge_statistics()
        return jsonify({
            'status': 'success',
            'stats': stats
        })
    except Exception as e:
        return jsonify({
            'status': 'error',
            'message': f'获取统计信息失败: {str(e)}'
        }), 500

@app.route('/api/run_rust_code', methods=['POST'])
@login_required
def run_rust_code():
    """运行Rust代码并返回结果"""
    try:
        data = request.get_json()
        code = data.get('code', '')
        if not code:
            return jsonify({'status': 'error', 'message': '代码不能为空'}), 400
        # 写入临时文件
        with NamedTemporaryFile('w', encoding='utf-8', suffix='.rs', delete=False) as f:
            f.write(code)
            rust_file = f.name
        exe_file = rust_file[:-3] + '.exe'
        # 编译
        compile_proc = subprocess.run(['rustc', rust_file, '-o', exe_file], capture_output=True, text=True, timeout=10)
        if compile_proc.returncode != 0:
            return jsonify({'status': 'error', 'message': '编译错误', 'stderr': compile_proc.stderr})
        # 运行
        run_proc = subprocess.run([exe_file], capture_output=True, text=True, timeout=5)
        return jsonify({'status': 'success', 'stdout': run_proc.stdout, 'stderr': run_proc.stderr})
    except subprocess.TimeoutExpired:
        return jsonify({'status': 'error', 'message': '运行超时'}), 500
    except Exception as e:
        return jsonify({'status': 'error', 'message': f'运行失败: {str(e)}'}), 500

@app.route('/api/analyze_rust_code', methods=['POST'])
@login_required
def analyze_rust_code():
    """调用大模型分析Rust代码，返回分析结果"""
    from answer_generator import AnswerGenerator
    try:
        data = request.get_json()
        code = data.get('code', '')
        examples_raw = data.get('examples', '')
        if not code:
            return jsonify({'status': 'error', 'message': '代码不能为空'}), 400
        # 解析 examples
        try:
            examples = json.loads(examples_raw) if examples_raw else {}
        except Exception as e:
            examples = {}
        code_practice = examples.get('code_practice', '')
        comprehensive_case = examples.get('comprehensive_case', '')
        question_content = data.get('question', '')  # 从code_practice界面获取题目内容
        
        # 判断分析类型：
        # - 如果请求中显式包含了'question'字段（即使为空），说明来自code_practice，使用4个部分
        # - 如果没有'question'字段，说明来自code_editor，使用5个部分
        is_code_practice = 'question' in data
        
        # 构造题目内容部分
        if is_code_practice:
            # code_practice界面：使用传入的question内容
            question_text = question_content if question_content else ''
        else:
            # code_editor界面：使用examples中的code_practice和comprehensive_case
            question_text = code_practice + '\n' + comprehensive_case if (code_practice or comprehensive_case) else ''
        
        # 根据分析类型构造不同的prompt
        if is_code_practice:
            # code_practice界面：4个部分
            prompt = f"""
你是一个专业的Rust编程题目批改与优化专家。请根据下方的【题目内容】和【用户提交代码】，完成如下分析：

【题目内容】
{question_text}

【用户提交代码】
```rust
{code}
```

请严格按照以下结构输出（只包含这5个部分）：

1. **正确性分析**：判断代码是否正确，是否能通过编译和运行。如果有错误，请详细指出错误位置、错误原因，并给出具体的修改建议。

2. **代码改进建议**：从代码风格、性能优化、安全性、可读性、Rust最佳实践等角度，给出具体可操作的改进建议。如果代码已经很好，也要指出可以进一步优化的方向。

3. **相关知识点**：基于当前代码和题目，列出建议学习的相关知识点，每个知识点用分点说明。

4. **更难的题目**：建议更难的练习题目或扩展功能，每个建议用分点说明。

5. **正向反馈**：指出该代码的优点、亮点或值得肯定的地方，鼓励用户继续学习。要具体、真诚、有建设性。

注意：
- 请用分点详细说明，内容要简明、专业、鼓励性强。每个部分都要有实质性内容。
- 第3部分和第4部分应该是独立的部分，不要使用层级编号（如3.1、3.2），而是直接使用3和4。
"""
        else:
            # code_editor界面：5个部分（保持原来的格式）
            prompt = f"""
你是一个专业的Rust编程题目批改与优化专家。请根据下方的【题目内容】和【用户提交代码】，完成如下分析：

{f'【题目内容】\n{question_text}\n' if question_text else '【说明】没有提供题目内容，请直接分析代码本身。\n'}
【用户提交代码】
```rust
{code}
```

请严格按照以下结构输出：

1. **题目归属判断**：判断该代码最有可能是针对哪一道题目（请给出题号和题目标题/内容，若无法判断请说明原因）。
2. **正确性分析**：判断代码是否正确，若有错误请详细指出并给出修改建议。
3. **优化建议**：从风格、性能、安全性等角度给出具体可操作的优化建议。
4. **完美代码示例**：给出针对该题目的最优/标准Rust代码（用```rust代码块包裹）。
5. **正向反馈**：指出该代码的优点、亮点或值得肯定的地方，鼓励用户继续学习。

请用分点详细说明，内容要简明、专业、鼓励性强。
"""
        ag = AnswerGenerator()
        
        # 优先使用星河大模型进行代码分析
        primary_model = model_status.get_primary_model()
        if model_status.status[primary_model]['available']:
            try:
                # 使用星河大模型分析
                response = ag.xinghe_client.chat.completions.create(
                    model="ernie-4.5-turbo-vl",
                    messages=[
                        {"role": "system", "content": "你是一个专业的Rust编程题目批改与优化专家。"},
                        {"role": "user", "content": prompt}
                    ],
                    max_tokens=2000,
                    temperature=0.7
                )
                content = response.choices[0].message.content
                model_status.update_status('xinghe', True)
                return jsonify({'status': 'success', 'result': content, 'model': 'xinghe'})
            except Exception as e:
                print(f"星河大模型代码分析失败: {e}")
                model_status.update_status('xinghe', False, str(e))
        
        # 使用备用模型进行代码分析
        fallback_models = model_status.get_fallback_models()
        for model_name in fallback_models:
            try:
                if model_name == 'gemini':
                    # 使用Gemini分析
                    gemini_payload = {
                        "contents": [
                            {"parts": [
                                {"text": prompt}
                            ]}
                        ]
                    }
                    headers = {
                        "Content-Type": "application/json",
                        "X-goog-api-key": ag.gemini_api_key
                    }
                    import requests
                    resp = requests.post(ag.gemini_url, json=gemini_payload, headers=headers, timeout=30)
                    if resp.status_code == 200:
                        data = resp.json()
                        if "candidates" in data and data["candidates"]:
                            content = data["candidates"][0]["content"]["parts"][0]["text"]
                            model_status.update_status('gemini', True)
                            return jsonify({'status': 'success', 'result': content, 'model': 'gemini'})
                        else:
                            raise Exception("Gemini无有效分析结果")
                    else:
                        raise Exception(f"Gemini API错误: {resp.status_code} {resp.text}")
                        
                elif model_name == 'openai':
                    # 使用OpenAI分析
                    response = ag.openai_client.chat.completions.create(
                        model="gpt-3.5-turbo",
                        messages=[
                            {"role": "system", "content": "你是一个专业的Rust编程题目批改与优化专家。"},
                            {"role": "user", "content": prompt}
                        ],
                        max_tokens=2000,
                        temperature=0.7
                    )
                    content = response.choices[0].message.content
                    model_status.update_status('openai', True)
                    return jsonify({'status': 'success', 'result': content, 'model': 'openai'})
                    
                elif model_name == 'deepseek':
                    # 使用DeepSeek分析
                    response = ag.deepseek_client.chat.completions.create(
                        model="deepseek-chat",
                        messages=[
                            {"role": "system", "content": "你是一个专业的Rust编程题目批改与优化专家。"},
                            {"role": "user", "content": prompt}
                        ],
                        max_tokens=2000,
                        temperature=0.7
                    )
                    content = response.choices[0].message.content
                    model_status.update_status('deepseek', True)
                    return jsonify({'status': 'success', 'result': content, 'model': 'deepseek'})
                    
            except Exception as e:
                print(f"{model_name}代码分析失败: {e}")
                model_status.update_status(model_name, False, str(e))
                continue
        
        # 所有模型都失败，返回错误
        return jsonify({'status': 'error', 'message': '所有AI模型都不可用，无法进行代码分析'})
    except Exception as e:
        return jsonify({'status': 'error', 'message': f'服务器错误: {str(e)}'})

@app.route('/api/generate_question', methods=['POST'])
@login_required
def generate_question():
    """生成Rust编程练习题目"""
    from answer_generator import AnswerGenerator
    try:
        data = request.get_json()
        knowledge_points = data.get('knowledge_points', [])
        difficulty = data.get('difficulty', 'medium')
        
        if not knowledge_points:
            return jsonify({'status': 'error', 'message': '请至少选择一个知识点'}), 400
        
        # 构造生成题目的prompt
        knowledge_points_str = '、'.join(knowledge_points)
        prompt = f"""
你是一个专业的Rust编程教育专家。请根据以下要求生成一道Rust编程练习题目：

知识点：{knowledge_points_str}
难度：{difficulty}

请生成一道完整的编程题目，包括：
1. 题目描述（清晰明确）
2. 初始代码框架（包含必要的函数签名和注释）
3. 标准答案（完整的、可运行的Rust代码）
4. 详细解析（解释解题思路和关键知识点）

请以JSON格式返回，格式如下：
{{
    "question": "题目描述",
    "code": "初始代码框架",
    "answer": "标准答案代码",
    "explanation": "详细解析"
}}
"""
        ag = AnswerGenerator()
        
        # 优先使用星河大模型生成题目
        primary_model = model_status.get_primary_model()
        if model_status.status[primary_model]['available']:
            try:
                response = ag.xinghe_client.chat.completions.create(
                    model="ernie-4.5-turbo-vl",
                    messages=[
                        {"role": "system", "content": "你是一个专业的Rust编程教育专家。请严格按照JSON格式返回题目。"},
                        {"role": "user", "content": prompt}
                    ],
                    max_tokens=2000,
                    temperature=0.7
                )
                content = response.choices[0].message.content
                model_status.update_status('xinghe', True)
                
                # 尝试解析JSON
                try:
                    # 移除可能的markdown代码块标记
                    content = content.strip()
                    if content.startswith('```'):
                        content = content.split('```')[1]
                        if content.startswith('json'):
                            content = content[4:]
                        content = content.strip()
                    if content.endswith('```'):
                        content = content.rsplit('```', 1)[0].strip()
                    
                    question_data = json.loads(content)
                    return jsonify({
                        'status': 'success',
                        'question': question_data,
                        'model': 'xinghe'
                    })
                except json.JSONDecodeError:
                    # 如果无法解析JSON，尝试从文本中提取
                    # 这里可以添加更复杂的解析逻辑
                    return jsonify({
                        'status': 'success',
                        'question': {
                            'question': content,
                            'code': '// 请在此编写代码\nfn main() {\n    \n}',
                            'answer': '// 标准答案\nfn main() {\n    println!("Hello, Rust!");\n}',
                            'explanation': '请参考AI生成的题目内容'
                        },
                        'model': 'xinghe'
                    })
            except Exception as e:
                print(f"星河大模型生成题目失败: {e}")
                model_status.update_status('xinghe', False, str(e))
        
        # 使用备用模型生成题目
        fallback_models = model_status.get_fallback_models()
        for model_name in fallback_models:
            try:
                if model_name == 'gemini':
                    gemini_payload = {
                        "contents": [
                            {"parts": [{"text": prompt}]}
                        ]
                    }
                    headers = {
                        "Content-Type": "application/json",
                        "X-goog-api-key": ag.gemini_api_key
                    }
                    import requests
                    resp = requests.post(ag.gemini_url, json=gemini_payload, headers=headers, timeout=30)
                    if resp.status_code == 200:
                        data = resp.json()
                        if "candidates" in data and data["candidates"]:
                            content = data["candidates"][0]["content"]["parts"][0]["text"]
                            model_status.update_status('gemini', True)
                            # 解析响应（简化处理）
                            return jsonify({
                                'status': 'success',
                                'question': {
                                    'question': f'请使用以下知识点编写Rust代码：{knowledge_points_str}',
                                    'code': '// 请在此编写代码\nfn main() {\n    \n}',
                                    'answer': '// 标准答案\nfn main() {\n    println!("Hello, Rust!");\n}',
                                    'explanation': content
                                },
                                'model': 'gemini'
                            })
                elif model_name == 'openai':
                    response = ag.openai_client.chat.completions.create(
                        model="gpt-3.5-turbo",
                        messages=[
                            {"role": "system", "content": "你是一个专业的Rust编程教育专家。请严格按照JSON格式返回题目。"},
                            {"role": "user", "content": prompt}
                        ],
                        max_tokens=2000,
                        temperature=0.7
                    )
                    content = response.choices[0].message.content
                    model_status.update_status('openai', True)
                    # 解析响应（简化处理）
                    try:
                        content = content.strip()
                        if content.startswith('```'):
                            content = content.split('```')[1]
                            if content.startswith('json'):
                                content = content[4:]
                            content = content.strip()
                        question_data = json.loads(content)
                        return jsonify({
                            'status': 'success',
                            'question': question_data,
                            'model': 'openai'
                        })
                    except json.JSONDecodeError:
                        return jsonify({
                            'status': 'success',
                            'question': {
                                'question': content,
                                'code': '// 请在此编写代码\nfn main() {\n    \n}',
                                'answer': '// 标准答案\nfn main() {\n    println!("Hello, Rust!");\n}',
                                'explanation': '请参考AI生成的题目内容'
                            },
                            'model': 'openai'
                        })
                elif model_name == 'deepseek':
                    response = ag.deepseek_client.chat.completions.create(
                        model="deepseek-chat",
                        messages=[
                            {"role": "system", "content": "你是一个专业的Rust编程教育专家。请严格按照JSON格式返回题目。"},
                            {"role": "user", "content": prompt}
                        ],
                        max_tokens=2000,
                        temperature=0.7
                    )
                    content = response.choices[0].message.content
                    model_status.update_status('deepseek', True)
                    # 解析响应（简化处理）
                    try:
                        content = content.strip()
                        if content.startswith('```'):
                            content = content.split('```')[1]
                            if content.startswith('json'):
                                content = content[4:]
                            content = content.strip()
                        question_data = json.loads(content)
                        return jsonify({
                            'status': 'success',
                            'question': question_data,
                            'model': 'deepseek'
                        })
                    except json.JSONDecodeError:
                        return jsonify({
                            'status': 'success',
                            'question': {
                                'question': content,
                                'code': '// 请在此编写代码\nfn main() {\n    \n}',
                                'answer': '// 标准答案\nfn main() {\n    println!("Hello, Rust!");\n}',
                                'explanation': '请参考AI生成的题目内容'
                            },
                            'model': 'deepseek'
                        })
            except Exception as e:
                print(f"{model_name}生成题目失败: {e}")
                model_status.update_status(model_name, False, str(e))
                continue
        
        # 所有模型都失败，返回错误
        return jsonify({'status': 'error', 'message': '所有AI模型都不可用，无法生成题目'})
    except Exception as e:
        return jsonify({'status': 'error', 'message': f'服务器错误: {str(e)}'})

@app.errorhandler(404)
def not_found(error):
    return jsonify({
        'status': 'error',
        'message': '接口不存在'
    }), 404

@app.errorhandler(500)
def internal_error(error):
    return jsonify({
        'status': 'error',
        'message': '服务器内部错误'
    }), 500

if __name__ == '__main__':
    # 确保静态文件目录存在
    os.makedirs('static/css', exist_ok=True)
    os.makedirs('static/js', exist_ok=True)
    os.makedirs('templates', exist_ok=True)
    

    # 获取端口，支持GitCode AI Space环境变量
    port = int(os.environ.get("PORT", 5000))
    
    print("启动Rust知识解释智能体Web服务...")
    print(f"访问地址: http://0.0.0.0:{port}")
    print(f"API文档: http://0.0.0.0:{port}/api/status")
    
    app.run(host='0.0.0.0', port=port, debug=True) 

